`include "fft_core_h.v"

module tb_cpsfft();
  
  reg  [31:0] PWDATA;
  reg  [ 1:0] PADDR;
  reg  PSEL, PENABLE, PWRITE, PRESETn, clk;
  
  wire [31:0] PRDATA;
  wire PREADY;
  
  reg [31:0] config_data, golden_config;
  
  localparam MEM_SIZE = 16;
  reg [31:0] mem [0 : MEM_SIZE - 1];
  reg [31:0] golden_mem [0 : MEM_SIZE - 1];
  
  reg [31:0] mem_input [0 : MEM_SIZE - 1];
  
  initial
  begin
    mem_input[0] = {16'sd328,16'sd655};
    mem_input[1] = {16'sd655,16'sd983};
    mem_input[2] = {16'sd983,16'sd1311};
    mem_input[3] = {16'sd1311,16'sd1638};
    mem_input[4] = {16'sd1638,16'sd1966};
    mem_input[5] = {16'sd1966,16'sd2294};
    mem_input[6] = {16'sd2294,16'sd2621};
    mem_input[7] = {16'sd2621,16'sd2949};
    
    mem_input[8] = {16'sd328,16'sd655};
    mem_input[9] = {16'sd655,16'sd983};
    mem_input[10] = {16'sd983,16'sd1311};
    mem_input[11] = {16'sd1311,16'sd1638};
    mem_input[12] = {16'sd1638,16'sd1966};
    mem_input[13] = {16'sd1966,16'sd2294};
    mem_input[14] = {16'sd2294,16'sd2621};
    mem_input[15] = {16'sd2621,16'sd2949};
  end
  
  task reset;
    begin
      PRESETn = 0;
      clk = 0;
      PSEL = 0;
      PWDATA = 32'd0;
      PADDR = 2'b00;
      PENABLE = 0;
      PWRITE = 0;
      #50
      PRESETn = 1;
    end
  endtask
  
  task apb_write;
    input [31:0] data;
    input [ 1:0] addr;
    begin
      @(negedge clk);
      PSEL = 1;
      PENABLE = 0;
      PWRITE = 1;
      PADDR = addr;
      PWDATA = data;
      @(posedge clk);
      PENABLE = 1;
      wait(PREADY);
      @(posedge clk)
      PSEL = 0;
      PENABLE = 0;
    end
  endtask
  
  task apb_read;
    input  [ 1:0] addr;
    output [31:0] data;
    begin
      @(negedge clk);
      PSEL = 1;
      PENABLE = 0;
      PADDR = addr;
      PWRITE = 0;
      @(posedge clk);
      PENABLE = 1;
      wait(PREADY);
      @(posedge clk);
      data = PRDATA;
      PSEL = 0;
      PENABLE = 0;
    end
  endtask
  
  task config_core;
    input [31:0] config_data;
    begin
      apb_write(config_data, `CONFIG_REG);
    end
  endtask
  
  task read_config;
    output [31:0] config_data;
    begin
      apb_read(`CONFIG_REG, config_data);
    end
  endtask
  
  task init_mem;
    integer i;
    begin
      for(i = 0; i < MEM_SIZE; i = i + 1)
        begin
          apb_write(mem_input[i], `MEM_WRITE);
          golden_mem[i] = mem_input[i];
        end
    end
  endtask
  
  task read_mem;
    integer i;
    begin
      for(i = 0; i < MEM_SIZE; i = i + 1)
        begin
          apb_read(`MEM_READ, mem[i]);
        end
    end
  endtask
  
  task display_mem;
    integer i;
    real x, y;
    begin
      $display("Memory");
      $display("Addr\tData");
      for(i = 0; i < MEM_SIZE; i = i + 1)
        begin
          x = mem[i][31:16]/32768.0;
          y = mem[i][15:0]/32768.0;
          $display("%0d\t%0x\t%f\t%f",i, mem[i], x, y);
        end
    end
  endtask
  
  task check_result;
    input [31:0] result;
    input [31:0] golden_result;
   input integer test_number;
   begin
     if(result != golden_result)
        begin
          $display("Test Case %0d failed!", test_number);
          $display("Expected:%x", golden_result);
          $display("Obtained:%x", result);
          $finish;
       end
     else
       $display("Test Case %0d passed!!", test_number);
   end
  endtask
  
  task check_mem;
    input integer test_number;
    integer i;
    begin
      for(i = 0; i < MEM_SIZE; i = i + 1)
       begin
         if(mem[i] != golden_mem[i])
           begin
             $display("Memory Check %0d failed!", test_number);
             $display("The first error is in position %d of memory",i);
             $display("Expected :%x",golden_mem[i]);
            $display("Obtained :%x",mem[i]);
            $stop;
           end
       end
        $display("Memory Check %0d passed!!", test_number);
    end
  endtask
  
  CPSFFT CPS_FFT
  (
    .PRDATA     ( PRDATA  ),   
    .PREADY     ( PREADY  ),                                
    .PSLVERR    ( PSLERR  ),                              
    .INT_CPSFFT ( INT_FFT ),                               
    .PWDATA     ( PWDATA  ),               
    .PADDR      ( PADDR   ),                            
    .PSEL       ( PSEL    ),                                   
    .PENABLE    ( PENABLE ),                                
    .PWRITE     ( PWRITE  ),                                 
    .PCLK       ( clk     ),                                   
    .PRESETn    ( PRESETn ) 
  );
  
  initial
    begin
      reset;
      
      //=============================================================================
      //  0) Test Case 0
      //     - Operation: Read and Write in Memory and Configuration Register
      //     - Description: This test configure the fft core to save the input data 
      //       in reversed order and read these data in natural order
      //=============================================================================
      golden_config = 32'h00_00_00_10; 
      config_core(golden_config);//Reversed order configuration
      init_mem;
      read_config(config_data);
      check_result(config_data, golden_config, 0);
      
      //config_core(32'h00_00_00_10);
      read_mem;
      check_mem(0);
      display_mem;
      config_core(32'h00_00_00_1d);
      @(posedge INT_FFT);
      display_mem;
      $stop;
      //init_mem;
      //config_core(32'h00_00_00_15);
      //@(posedge INT_FFT);
      //read_mem;
      //display_mem;
      //$stop;
    end
    
  always #10
    clk = !clk;
endmodule

